home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’94 / [√] Distribution Restricted! / Christian Ruse / Fourier Paper + Apps / nih-image154_source.sea / V1.54 Source / Stacks.p < prev    next >
Text File  |  1994-01-27  |  45KB  |  1,714 lines

  1. unit Stacks;
  2.  
  3. interface
  4.  
  5.     uses
  6.         QuickDraw, Palettes, QDOffscreen, PictUtil, PrintTraps, globals, Utilities, Graphics, Analysis, Camera, file1, file2, filters, sound, lut;
  7.  
  8.     function MakeStackFromWindow: boolean;
  9.     procedure MakeStack;
  10.     procedure MakeWindowsFromStack;
  11.     function AddSlice (update: boolean): boolean;
  12.     procedure DeleteSlice;
  13.     procedure ShowNextSlice (item: integer);
  14.     procedure ShowFirstOrLastSlice (ich: integer);
  15.     procedure DoResliceOptions;
  16.     procedure Reslice;
  17.     procedure Animate;
  18.     procedure MakeMovie;
  19.     procedure CaptureFrames;
  20.     procedure MakeMontage;
  21.     procedure ConvertRGBToEightBitColor (Capturing: boolean);
  22.     procedure ConvertEightBitColorToRGB;
  23.     procedure CaptureColor;
  24.     procedure AverageSlices;
  25.     procedure ConvertRGBToHSV;
  26.  
  27.  
  28. implementation
  29.  
  30.  
  31.     function MakeStackFromWindow: boolean;
  32.     begin
  33.         with info^ do begin
  34.                 StackInfo := StackInfoPtr(NewPtr(SizeOf(StackInfoRec)));
  35.                 if StackInfo = nil then begin
  36.                         MakeStackFromWindow := false;
  37.                         exit(MakeStackFromWindow);
  38.                     end;
  39.                 with StackInfo^ do begin
  40.                         nSlices := 1;
  41.                         CurrentSlice := 1;
  42.                         PicBaseH[1] := PicBaseHandle;
  43.                         SliceSpacing := 0.0;
  44.                         LoopTime := 0.0;
  45.                     end;
  46.                 PictureType := NewPicture;
  47.                 MakeStackFromWindow := true;
  48.             end;
  49.     end;
  50.  
  51.  
  52.     procedure MakeStack;
  53.         var
  54.             ok, isStack: boolean;
  55.             i, result: integer;
  56.             TempInfo, SaveInfo: InfoPtr;
  57.             str: str255;
  58.     begin
  59.         if not AllSameSize then begin
  60.                 PutMessage('All currently open images must be the same size to make a stack.');
  61.                 exit(MakeStack);
  62.             end;
  63.         isStack := false;
  64.         for i := 1 to nPics do begin
  65.                 TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  66.                 isStack := isStack or (TempInfo^.StackInfo <> nil);
  67.             end;
  68.         if isStack then begin
  69.                 PutMessage('All stacks must be closed before making a new stack.');
  70.                 exit(MakeStack);
  71.             end;
  72.         if nPics > MaxSlices then begin
  73.                 NumToString(MaxSlices, str);
  74.                 PutMessage(concat('Maximun stack size is ', str, ' slices.'));
  75.                 exit(MakeStack);
  76.             end;
  77.         StopDigitizing;
  78.         DisableDensitySlice;
  79.         SelectWindow(PicWindow[1]);
  80.         Info := pointer(WindowPeek(PicWindow[1])^.RefCon);
  81.         ActivateWindow;
  82.         KillRoi;
  83.         UnZoom;
  84.         if not MakeStackFromWindow then
  85.             exit(MakeStack);
  86.         with info^ do begin
  87.                 StackInfo^.nSlices := nPics;
  88.                 title := 'Stack';
  89.                 UpdateTitleBar;
  90.                 Revertable := false;
  91.             end;
  92.         SaveInfo := Info;
  93.         MakingStack := true;
  94.         ShowWatch;
  95.         for i := 2 to nPics do begin
  96.                 TempInfo := pointer(WindowPeek(PicWindow[2])^.RefCon);
  97.                 with TempInfo^ do begin
  98.                         hunlock(PicBaseHandle);
  99.                         info^.StackInfo^.PicBaseH[i] := PicBaseHandle;
  100.                     end;
  101.                 result := CloseAWindow(PicWindow[2]);
  102.                 Info := SaveInfo;
  103.             end;
  104.         with info^ do
  105.             UpdateWindowsMenuItem(PixMapSize * StackInfo^.nSlices, title, 1);
  106.         MakingStack := false;
  107.     end;
  108.  
  109.  
  110.     function AddSlice (update: boolean): boolean;
  111.         var
  112.             i: integer;
  113.             h: handle;
  114.             isRoi: boolean;
  115.     begin
  116.         with info^, info^.StackInfo^ do begin
  117.                 AddSlice := false;
  118.                 if nSlices = MaxSlices then
  119.                     exit(AddSlice);
  120.                 isRoi := RoiShowing;
  121.                 if isRoi then
  122.                     KillRoi;
  123.                 h := GetBigHandle(PixMapSize);
  124.                 if h = nil then begin
  125.                         PutMessage('Not enough memory available to add a slice to this stack.');
  126.                         macro := false;
  127.                         exit(AddSlice);
  128.                     end;
  129.                 for i := nSlices downto CurrentSlice + 1 do
  130.                     PicBaseH[i + 1] := PicBaseH[i];
  131.                 nSlices := nSlices + 1;
  132.                 CurrentSlice := CurrentSlice + 1;
  133.                 PicBaseH[CurrentSlice] := h;
  134.                 SelectSlice(CurrentSlice);
  135.                 if Update then begin
  136.                         SelectAll(false);
  137.                         DoOperation(EraseOp);
  138.                         UpdatePicWindow;
  139.                     end;
  140.                 UpdateTitleBar;
  141.                 if isRoi then
  142.                     RestoreRoi;
  143.                 WhatToUndo := NothingToUndo;
  144.                 AddSlice := true;
  145.                 changes := true;
  146.                 PictureType := NewPicture;
  147.                 UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum);
  148.             end;
  149.     end;
  150.  
  151.  
  152.     procedure DeleteSlice;
  153.         var
  154.             SliceToDelete, NextSlice, i: integer;
  155.             isRoi: boolean;
  156.     begin
  157.         with info^, info^.StackInfo^ do begin
  158.                 if nSlices = 1 then begin
  159.                         WhatToUndo := NothingToUndo;
  160.                         exit(DeleteSlice);
  161.                     end;
  162.                 isRoi := RoiShowing;
  163.                 if isRoi then
  164.                     KillRoi;
  165.                 SetupUndo;
  166.                 WhatToUndo := UndoSliceDelete;
  167.                 SliceToDelete := CurrentSlice;
  168.                 if CurrentSlice = 1 then begin
  169.                         NextSlice := 2;
  170.                         WhatToUndo := UndoFirstSliceDelete;
  171.                     end
  172.                 else
  173.                     NextSlice := CurrentSlice - 1;
  174.                 SelectSlice(NextSlice);
  175.                 UpdatePicWindow;
  176.                 DisposHandle(PicBaseH[SliceToDelete]);
  177.                 for i := SliceToDelete to nSlices - 1 do
  178.                     PicBaseH[i] := PicBaseH[i + 1];
  179.                 nSlices := nSlices - 1;
  180.                 if CurrentSlice <> 1 then
  181.                     CurrentSlice := CurrentSlice - 1;
  182.                 UpdateTitleBar;
  183.                 if isRoi then
  184.                     RestoreRoi;
  185.                 changes := true;
  186.                 UpdateWindowsMenuItem(PixMapSize * nSlices, title, PicNum);
  187.             end;
  188.     end;
  189.  
  190.  
  191.     procedure MakeWindowsFromStack;
  192.         var
  193.             i, ignore, N: integer;
  194.             SaveInfo: InfoPtr;
  195.             tmp: longint;
  196.  
  197.         function MakeName (i: integer): str255;
  198.             var
  199.                 str: str255;
  200.         begin
  201.             RealToString(i, 3, 0, str);
  202.             if str[1] = ' ' then
  203.                 str[1] := '0';
  204.             if str[2] = ' ' then
  205.                 str[2] := '0';
  206.             MakeName := str;
  207.         end;
  208.  
  209.     begin
  210.         N := info^.StackInfo^.nSlices;
  211.         tmp := SizeOf(PicInfo);
  212.         if MaxBlock < (MinFree + info^.ImageSize + (SizeOf(PicInfo) + 2000) * LongInt(N)) then begin
  213.                 PutMessage('There is not enough memory available to convert this stack to windows.');
  214.                 exit(MakeWindowsFromStack);
  215.             end;
  216.         SaveInfo := Info;
  217.         KillRoi;
  218.         for i := 1 to N - 1 do begin
  219.                 SelectSlice(1);
  220.                 info^.StackInfo^.CurrentSlice := 1;
  221.                 if not Duplicate(MakeName(i), false) then
  222.                     exit(MakeWindowsFromStack);
  223.                 info := SaveInfo;
  224.                 DeleteSlice;
  225.             end;
  226.         if Duplicate(MakeName(N), false) then begin
  227.                 info := SaveInfo;
  228.                 info^.changes := false;
  229.                 ignore := CloseAWindow(info^.wptr);
  230.             end;
  231.     end;
  232.  
  233.  
  234.     procedure ShowNextSlice (item: integer);
  235.         var
  236.             isRoi: boolean;
  237.     begin
  238.         with info^, info^.StackInfo^ do begin
  239.                 if item = NextSliceItem then begin
  240.                         CurrentSlice := CurrentSlice + 1;
  241.                         if CurrentSlice > nSlices then
  242.                             CurrentSlice := nSlices;
  243.                     end
  244.                 else begin
  245.                         CurrentSlice := CurrentSlice - 1;
  246.                         if CurrentSlice < 1 then
  247.                             CurrentSlice := 1;
  248.                     end;
  249.                 isRoi := RoiShowing;
  250.                 if isRoi then
  251.                     KillRoi;
  252.                 SelectSlice(CurrentSlice);
  253.                 UpdatePicWindow;
  254.                 UpdateTitleBar;
  255.                 WhatToUndo := NothingToUndo;
  256.                 if isRoi then
  257.                     RestoreRoi;
  258.             end;
  259.     end;
  260.  
  261.  
  262.     procedure ShowFirstOrLastSlice (ich: integer);
  263.         var
  264.             isRoi: boolean;
  265.     begin
  266.         with info^, info^.StackInfo^ do begin
  267.                 if ich = EndKey then
  268.                     CurrentSlice := nSlices
  269.                 else
  270.                     CurrentSlice := 1;
  271.                 isRoi := RoiShowing;
  272.                 if isRoi then
  273.                     KillRoi;
  274.                 SelectSlice(CurrentSlice);
  275.                 UpdatePicWindow;
  276.                 UpdateTitleBar;
  277.                 WhatToUndo := NothingToUndo;
  278.                 if isRoi then
  279.                     RestoreRoi;
  280.             end;
  281.     end;
  282.  
  283.  
  284.     procedure DoResliceOptions;
  285.         var
  286.             default, tmp: extended;
  287.             Canceled: boolean;
  288.             prompt: str255;
  289.     begin
  290.         with info^.StackInfo^, info^ do begin
  291.                 if SliceSpacing = 0.0 then
  292.                     default := 1.0
  293.                 else begin
  294.                         if SpatiallyCalibrated then
  295.                             default := SliceSpacing / xSpatialScale
  296.                         else
  297.                             default := SliceSpacing;
  298.                     end;
  299.                 tmp := GetReal(concat('Slice Spacing(', xUnit, '):'), default, Canceled);
  300.                 if not Canceled and (tmp > 0.0) then begin
  301.                         if SpatiallyCalibrated then
  302.                             SliceSpacing := tmp * xSpatialScale
  303.                         else
  304.                             SliceSpacing := tmp;
  305.                     end;
  306.             end;
  307.     end;
  308.  
  309.  
  310.     procedure GetSlice (xstart, ystart, start: real; angle: extended; count: integer; var line: LineType);
  311.         var
  312.             i: integer;
  313.             x, y, xinc, yinc: extended;
  314.             IntegerStart: boolean;
  315.     begin
  316.         IntegerStart := (xstart = trunc(xstart)) and (ystart = trunc(ystart));
  317.         if IntegerStart and (angle = 0.0) then begin
  318.                 GetLine(trunc(xstart), trunc(ystart), count, line);
  319.                 exit(GetSlice);
  320.             end;
  321.         if IntegerStart and (angle = 270.0) then begin
  322.                 GetColumn(trunc(xstart), trunc(ystart), count, line);
  323.                 exit(GetSlice);
  324.             end;
  325.         angle := (angle / 180.0) * pi;
  326.         xinc := cos(angle);
  327.         yinc := -sin(angle);
  328.         x := xstart + start * xinc;
  329.         y := ystart + start * yinc;
  330.         for i := 0 to count - 1 do begin
  331.                 line[i] := round(GetInterpolatedPixel(x, y));
  332.                 x := x + xinc;
  333.                 y := y + yinc;
  334.             end;
  335.     end;
  336.  
  337.  
  338.     procedure Reslice;
  339.         var
  340.             DstWidth, DstHeight, nSlices: integer;
  341.             dstLeft, dstTop, y, i, j, LineLength: integer;
  342.             SaveWindowFlag, SaveMacro, HorizontalMode: boolean;
  343.             SaveHScale, SaveVScale, UncalibratedLineLength, CalibratedLineLength, angle: extended;
  344.             Stack, Reconstruction: InfoPtr;
  345.             aLine: LineType;
  346.             name, str1, str2: str255;
  347.             MaskRect: rect;
  348.             x1, y1, x2, y2, ulength, clength: real;
  349.  
  350.         procedure MakeRoi (Left, Top, Width, Height: integer);
  351.         begin
  352.             with info^ do begin
  353.                     RoiType := RectRoi;
  354.                     SetRect(RoiRect, left, top, left + width, top + height);
  355.                     MakeRegion;
  356.                     SetupUndo;
  357.                     RoiShowing := true;
  358.                 end;
  359.         end;
  360.  
  361.     begin
  362.         with info^, info^.StackInfo^ do begin
  363.                 if nSlices < 2 then begin
  364.                         PutMessage('Reslicing requires at least 2 slices.');
  365.                         macro := false;
  366.                         exit(Reslice);
  367.                     end;
  368.                 if not (RoiShowing and (RoiType = LineRoi)) then begin
  369.                         PutMessage('Please make a straight line selection first.');
  370.                         macro := false;
  371.                         exit(Reslice);
  372.                     end;
  373.                 Stack := info;
  374.                 GetLengthOrPerimeter(ulength, clength);
  375.                 LineLength := round(ulength);
  376.                 if LineLength = 0 then begin
  377.                         PutMessage('Line length cannot be zero.');
  378.                         macro := false;
  379.                         exit(Reslice);
  380.                     end;
  381.                 if SliceSpacing = 0.0 then
  382.                     DoResliceOptions;
  383.                 GetLoi(x1, y1, x2, y2);
  384.                 if (LAngle = 0.0) or (LAngle = 270.0) then
  385.                     if NotInBounds then
  386.                         exit(Reslice);
  387.                 HorizontalMode := not OptionKeyWasDown;
  388.                 if HorizontalMode then begin
  389.                         DstWidth := round(LineLength);
  390.                         DstHeight := round(nSlices * SliceSpacing);
  391.                         if DstHeight < nSlices then
  392.                             DstHeight := nSlices;
  393.                         dstLeft := 0;
  394.                         dstTop := round((dstHeight - nSlices) / 2);
  395.                     end
  396.                 else begin
  397.                         DstWidth := round(nSlices * SliceSpacing);
  398.                         if DstWidth < nSlices then
  399.                             DstWidth := nSlices;
  400.                         DstHeight := round(LineLength);
  401.                         dstLeft := round((dstWidth - nSlices) / 2);
  402.                         dstTop := 0;
  403.                     end;
  404.                 RealToString(y1, 3, 0, str1);
  405.                 RealToString(LAngle, 1, 2, str2);
  406.                 name := concat(str1, '-', str2);
  407.                 if not NewPicWindow(name, DstWidth, DstHeight) then
  408.                     exit(Reslice);
  409.                 Reconstruction := info;
  410.                 SaveWindowFlag := rsCreateNewWindow;
  411.                 SaveHScale := rsHScale;
  412.                 SaveVScale := rsVScale;
  413.                 rsCreateNewWindow := false;
  414.                 rsMethod := bilinear;
  415.                 for i := 1 to nSlices do begin
  416.                         Info := Stack;
  417.                         SelectSlice(i);
  418.                         GetSlice(x1, y1, 0.0, LAngle, LineLength, aLine);
  419.                         info := Reconstruction;
  420.                         if HorizontalMode then begin
  421.                                 PutLine(dstLeft, dstTop + nSlices - i, LineLength, aLine);
  422.                                 if i = 1 then {Draw extra line needed to get scaling to work right.}
  423.                                     PutLine(dstLeft, dstTop + nSlices, LineLength, aLine);
  424.                                 SetRect(MaskRect, dstLeft, dstTop + nSlices - i, dstLeft + LineLength, dstTop + nSlices - i + 1);
  425.                             end
  426.                         else begin
  427.                                 PutColumn(dstLeft + nSlices - i, dstTop, LineLength, aLine);
  428.                                 if i = 1 then {Draw extra line needed to get scaling to work right.}
  429.                                     PutLine(dstLeft + nSlices, dstTop, LineLength, aLine);
  430.                                 SetRect(MaskRect, dstLeft + nSlices - i, dstTop, dstLeft + nSlices - i + 1, dstTop + LineLength);
  431.                             end;
  432.                         UpdateScreen(MaskRect);
  433.                     end;
  434.                 if HorizontalMode then begin
  435.                         MakeRoi(dstLeft, dstTop, LineLength, nSlices);
  436.                         rsHScale := 1.0;
  437.                         rsVScale := SliceSpacing;
  438.                     end
  439.                 else begin
  440.                         MakeRoi(dstLeft, dstTop, nSlices, LineLength);
  441.                         rsHScale := SliceSpacing;
  442.                         rsVScale := 1.0;
  443.                     end;
  444.                 rsAngle := 0;
  445.                 SaveMacro := macro;
  446.                 macro := true;
  447.                 ScaleAndRotate;
  448.                 macro := SaveMacro;
  449.                 Info := Stack;
  450.                 SelectSlice(CurrentSlice);
  451.                 Info := Reconstruction;
  452.                 rsCreateNewWindow := SaveWindowFlag;
  453.                 rsHScale := SaveHScale;
  454.                 rsVScale := SaveVScale;
  455.                 KillRoi;
  456.             end;
  457.     end;
  458.  
  459.  
  460.     procedure Animate;
  461.         var
  462.             n, SaveN, fpsInterval, DelayCount: integer;
  463.             Event: EventRecord;
  464.             ch: char;
  465.             b: boolean;
  466.             SingleStep, GoForward, NewKeyDown, PhotoMode: boolean;
  467.             nFrames, StartTicks, NextTicks, SaveTicks, ticks: LongInt;
  468.             fps, seconds: extended;
  469.  
  470.         procedure ShowFPS (fps: extended);
  471.             var
  472.                 hstart, vstart, ivalue: integer;
  473.                 key: str255;
  474.         begin
  475.             if PhotoMode then
  476.                 exit(ShowFPS);
  477.             hstart := ValuesHStart;
  478.             vstart := ValuesVStart;
  479.             SetPort(ValuesWindow);
  480.             MoveTo(xValueLoc, vstart);
  481.             case DelayTicks of
  482.                 0: 
  483.                     key := '9 ';
  484.                 2: 
  485.                     key := '8 ';
  486.                 3: 
  487.                     key := '7 ';
  488.                 4: 
  489.                     key := '6 ';
  490.                 6: 
  491.                     key := '5 ';
  492.                 8: 
  493.                     key := '4 ';
  494.                 12: 
  495.                     key := '3 ';
  496.                 30: 
  497.                     key := '2 ';
  498.                 60: 
  499.                     key := '1 ';
  500.             end;
  501.             if SingleStep then begin
  502.                     if GoForward then
  503.                         key := '->'
  504.                     else
  505.                         key := '<-';
  506.                 end;
  507.             DrawString(key);
  508.             MoveTo(yValueLoc, vstart + 10);
  509.             DrawReal(fps, 1, 2);
  510.             DrawChar(' ');
  511.         end;
  512.  
  513.     begin
  514.         if info^.StackInfo = nil then begin
  515.                 PutMessage('Animation requires a stack.');
  516.                 exit(Animate);
  517.             end;
  518.         with info^, info^.StackInfo^ do begin
  519.                 if nSlices < 2 then begin
  520.                         PutMessage('Animation requires at least two "slices".');
  521.                         exit(Animate);
  522.                     end;
  523.                 KillRoi;
  524.                 PhotoMode := OptionKeyDown or OptionKeyWasDown;
  525.                 if PhotoMode then
  526.                     EraseScreen
  527.                 else begin
  528.                         ShowWatch;
  529.                         ShowMessage(concat('Use 1...9 keys to control speed', cr, 'Use arrow keys to single step', cr, 'Press mouse button to stop'));
  530.                     end;
  531.                 FlushEvents(EveryEvent, 0);
  532.                 fpsInterval := 10;
  533.                 SaveN := -1;
  534.                 n := 1;
  535.                 GoForward := true;
  536.                 SingleStep := false;
  537.                 nFrames := 0;
  538.                 StartTicks := TickCount;
  539.                 NextTicks := StartTicks;
  540.                 SaveTicks := StartTicks;
  541.                 if not PhotoMode then begin
  542.                         DrawLabels('key:', 'fps:', '');
  543.                         SetPort(ValuesWindow);
  544.                         TextSize(9);
  545.                         TextFont(Monaco);
  546.                         TextMode(SrcCopy);
  547.                     end;
  548.                 repeat
  549.                     b := WaitNextEvent(EveryEvent, Event, 0, nil);
  550.                     NewKeyDown := (event.what = KeyDown) or (event.what = AutoKey);
  551.                     if NewKeyDown then begin
  552.                             Ch := chr(BitAnd(Event.message, 127));
  553.                             SingleStep := false;
  554.                             case ord(ch) of
  555.                                 28, 44, 60, PageUp: {<-, <}
  556.                                     begin
  557.                                         SingleStep := true;
  558.                                         GoForward := false;
  559.                                         n := n - 1;
  560.                                         if n < 1 then
  561.                                             n := 1;
  562.                                         DelayTicks := 0
  563.                                     end; {left}
  564.                                 29, 46, 62, PageDown:  {->, >}
  565.                                     begin
  566.                                         SingleStep := true;
  567.                                         GoForward := true;
  568.                                         n := n + 1;
  569.                                         if n > nSlices then
  570.                                             n := nSlices;
  571.                                         DelayTicks := 0
  572.                                     end;  {right}
  573.                                 57: 
  574.                                     DelayTicks := 0;  {'9'-max speed}
  575.                                 56: 
  576.                                     DelayTicks := 2;  {'8'-30 fps}
  577.                                 55: 
  578.                                     DelayTicks := 3;  {'7'-20 fps}
  579.                                 54: 
  580.                                     DelayTicks := 4;  {'6'-15 fps}
  581.                                 53: 
  582.                                     DelayTicks := 6;  {'5'-10 fps}
  583.                                 52: 
  584.                                     DelayTicks := 8; {'4'-7.5 fps}
  585.                                 51: 
  586.                                     DelayTicks := 12; {'3'-5 fps}
  587.                                 50: 
  588.                                     DelayTicks := 30; {'2'-2 fps}
  589.                                 49: 
  590.                                     DelayTicks := 60; {'1'-1 fps}
  591.                                 otherwise
  592.                             end; {case}
  593.                             if DelayTicks > 12 then
  594.                                 fpsInterval := 2
  595.                             else if DelayTicks > 3 then
  596.                                 fpsInterval := 5
  597.                             else
  598.                                 fpsInterval := 10;
  599.                         end; {if NewKeyDown}
  600.                     if GoForward then begin
  601.                             if not SingleStep then
  602.                                 n := n + 1;
  603.                             if n > nSlices then begin
  604.                                     if OscillatingMovies then begin
  605.                                             n := nSlices - 1;
  606.                                             GoForward := false;
  607.                                         end
  608.                                     else
  609.                                         n := 1;
  610.                                 end;
  611.                         end
  612.                     else begin
  613.                             if not SingleStep then
  614.                                 n := n - 1;
  615.                             if n < 1 then begin
  616.                                     if OscillatingMovies then begin
  617.                                             n := 2;
  618.                                             Goforward := true;
  619.                                         end
  620.                                     else
  621.                                         n := nSlices;
  622.                                 end;
  623.                         end;
  624.                     CurrentSlice := n;
  625.                     SelectSlice(CurrentSlice);
  626.                     UpdatePicWindow;
  627.                     nFrames := nFrames + 1;
  628.                     if SingleStep then begin
  629.                             if (not OptionKeyWasDown) and (n <> SaveN) then begin
  630.                                     UpdateTitleBar;
  631.                                     SaveN := n;
  632.                                 end;
  633.                             ShowFPS(0.0);
  634.                         end
  635.                     else if (nFrames mod fpsInterval) = 0 then begin
  636.                             ticks := TickCount;
  637.                             seconds := (ticks - SaveTicks) / 60.0;
  638.                             if seconds <> 0.0 then
  639.                                 fps := fpsInterval / seconds
  640.                             else
  641.                                 fps := 0.0;
  642.                             ShowFPS(fps);
  643.                             SaveTicks := ticks;
  644.                         end;
  645.                     DelayCount := 0;
  646.                     if DelayTicks > 0 then begin
  647.                             repeat
  648.                                 ticks := TickCount;
  649.                             until ticks >= NextTicks;
  650.                             NextTicks := ticks + DelayTicks;
  651.                         end;
  652.                 until (event.what = MouseDown) or (event.what = osEvt);
  653.                 if PhotoMode then
  654.                     RestoreScreen;
  655.                 FlushEvents(EveryEvent, 0);
  656.             end; {with}
  657.     end;
  658.  
  659.  
  660.     procedure MakeMovie;
  661.         var
  662.             nFrames, wleft, wtop, width, height, frame, i: integer;
  663.             ignore, SaveFW: integer;
  664.             OutOfMemory: boolean;
  665.             DisplayPoint: point;
  666.             StartTicks, NextTicks, interval, ElapsedTime: LongInt;
  667.             SecondsBetweenFrames, seconds: extended;
  668.             frect: rect;
  669.             MainDevice: GDHandle;
  670.             SourcePixMap: PixMapHandle;
  671.             str1, str2, str3: str255;
  672.             Canceled: boolean;
  673.     begin
  674.         with info^ do begin
  675.                 if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  676.                         PutMessage('You must be capturing to make a movie.');
  677.                         exit(MakeMovie);
  678.                     end;
  679.                 StopDigitizing;
  680.                 if not (RoiShowing and (RoiType = RectRoi)) then begin
  681.                         PutMessage('Please make a rectangular selection first.');
  682.                         exit(MakeMovie);
  683.                     end;
  684.                 if NotInBounds then
  685.                     exit(MakeMovie);
  686.                 SaveFW := FramesWanted;
  687.                 FramesWanted := GetInt('Number of Frames:', FramesWanted, Canceled);
  688.                 if Canceled then begin
  689.                         FramesWanted := SaveFW;
  690.                         exit(MakeMovie);
  691.                     end;
  692.                 if FramesWanted < 1 then
  693.                     FramesWanted := 1;
  694.                 if FramesWanted > MaxSlices then
  695.                     FramesWanted := MaxSlices;
  696.                 with RoiRect do begin
  697.                         left := band(left + 1, $fffc);   {Word align}
  698.                         right := band(right + 2, $fffc);
  699.                         if right > PicRect.right then
  700.                             right := PicRect.right;
  701.                         MakeRegion;
  702.                         wleft := left;
  703.                         wtop := top;
  704.                         width := right - left;
  705.                         height := bottom - top;
  706.                     end;
  707.             end; {with info^}
  708.         if FrameGrabber = Scion then begin
  709.                 with DisplayPoint do begin
  710.                         h := PicLeftBase;
  711.                         v := PicTopBase;
  712.                     end;
  713.                 with frect do begin
  714.                         left := PicLeftBase + wleft;
  715.                         top := PicTopBase + wtop;
  716.                         right := left + width;
  717.                         bottom := top + height;
  718.                     end;
  719.             end
  720.         else
  721.             with frect do begin
  722.                     left := wleft;
  723.                     top := wtop;
  724.                     right := left + width;
  725.                     bottom := top + height;
  726.                 end;
  727.         if not NewPicWindow('Movie', width, height) then
  728.             exit(MakeMovie);
  729.         if not MakeStackFromWindow then
  730.             exit(MakeMovie);
  731.         nFrames := 1;
  732.         OutOfMemory := false;
  733.         while (nFrames < FramesWanted) and (not OutOfMemory) do begin
  734.                 OutOfMemory := not AddSlice(false);
  735.                 if not OutOfMemory then
  736.                     nFrames := nFrames + 1;
  737.             end;
  738.         if ExternalTrigger then
  739.             SecondsBetweenFrames := 0.0
  740.         else
  741.             SecondsBetweenFrames := GetReal('Delay Between Frames(seconds):', 0.0, Canceled);
  742.         if Canceled then
  743.             with info^ do begin
  744.                     changes := false;
  745.                     ignore := CloseAWindow(wptr);
  746.                     Exit(MakeMovie);
  747.                 end;
  748.         if SecondsBetweenFrames < 0.0 then
  749.             SecondsBetweenFrames := 0.0;
  750.         interval := round(60.0 * SecondsBetweenFrames);
  751.         if FrameGrabber = Scion then begin
  752.                 HideCursor;
  753.                 MainDevice := GetMainDevice;
  754.                 SourcePixMap := MainDevice^^.gdPMap;
  755.             end
  756.         else begin
  757.                 ShowWatch;
  758.                 SourcePixMap := fgPort^.portPixMap;
  759.                 ResetFrameGrabber;
  760.             end;
  761.         ShowTriggerMessage;
  762.         StartTicks := TickCount;
  763.         NextTicks := StartTicks;
  764.         with info^, info^.StackInfo^ do begin
  765.                 if Interval >= 30 then
  766.                     ShowMessage(CmdPeriodToStop)
  767.                 else
  768.                     DrawLabels('Frame:', 'Total:', '');
  769.                 for frame := 1 to nFrames do begin
  770.                         CurrentSlice := frame;
  771.                         SelectSlice(CurrentSlice);
  772.                         NextTicks := NextTicks + Interval;
  773.                         if FrameGrabber = Scion then begin
  774.                                 GetScionFrame(DisplayPoint);
  775.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  776.                             end
  777.                         else begin
  778.                                 if Interval >= 30 then
  779.                                     UpdateTitleBar
  780.                                 else
  781.                                     Show2Values(CurrentSlice, nSlices);
  782.                                 GetFrame;
  783.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  784.                                 if not BlindMovieCapture then
  785.                                     UpdatePicWindow;
  786.                             end;
  787.                         while TickCount < NextTicks do
  788.                             if CommandPeriod then begin
  789.                                     beep;
  790.                                     wait(60);
  791.                                     exit(MakeMovie);
  792.                                 end;
  793.                     end; {for}
  794.                 seconds := (TickCount - StartTicks) / 60.0;
  795.                 LoopTime := seconds;
  796.             end; {with}
  797.         RealToString(seconds, 1, 2, str1);
  798.         str1 := concat(long2str(nFrames), ' frames', cr, str1, ' seconds', cr);
  799.         RealToString(seconds / nFrames, 1, 3, str2);
  800.         str3 := concat(str1, str2, ' seconds/frame', cr);
  801.         if nFrames >= seconds then
  802.             ShowFrameRate(str3, StartTicks, nFrames)
  803.         else
  804.             ShowMessage(str3);
  805.         ShowFirstOrLastSlice(HomeKey);
  806.     end;
  807.  
  808.  
  809.     procedure CaptureFrames;
  810.         var
  811.             nFrames, wleft, wtop, width, height, i: integer;
  812.             ignore, SaveFW: integer;
  813.             OutOfMemory, AdvanceFrame, b: boolean;
  814.             DisplayPoint: point;
  815.             frect: rect;
  816.             MainDevice: GDHandle;
  817.             SourcePixMap: PixMapHandle;
  818.             Event: EventRecord;
  819.             ShutterSound: handle;
  820.             err: OSErr;
  821.  
  822.         procedure CheckButton;
  823.         begin
  824.             if Button and not AdvanceFrame then
  825.                 with Info^.StackInfo^ do begin
  826.                         AdvanceFrame := true;
  827.                         ShutterSound := GetResource('snd ', 100);
  828.                         if ShutterSound <> nil then
  829.                             err := SndPlay(nil, ShutterSound, false);
  830.                         if CurrentSlice < nSlices then begin
  831.                                 CurrentSlice := CurrentSlice + 1;
  832.                                 UpdateTitleBar;
  833.                                 CurrentSlice := CurrentSlice - 1;
  834.                             end;
  835.                     end;
  836.         end;
  837.  
  838.     begin
  839.         with info^ do begin
  840.                 if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  841.                         PutMessage('You must be capturing to capture frames.');
  842.                         exit(CaptureFrames);
  843.                     end;
  844.                 StopDigitizing;
  845.                 if not (RoiShowing and (RoiType = RectRoi)) then begin
  846.                         PutMessage('Please make a rectangular selection first.');
  847.                         exit(CaptureFrames);
  848.                     end;
  849.                 if NotInBounds then
  850.                     exit(CaptureFrames);
  851.                 SaveFW := FramesWanted;
  852.                 ShutterSound := nil;
  853.                 with RoiRect do begin
  854.                         left := band(left + 1, $fffc);   {Word align}
  855.                         right := band(right + 2, $fffc);
  856.                         if right > PicRect.right then
  857.                             right := PicRect.right;
  858.                         MakeRegion;
  859.                         wleft := left;
  860.                         wtop := top;
  861.                         width := right - left;
  862.                         height := bottom - top;
  863.                     end;
  864.             end; {with info^}
  865.         if FrameGrabber = Scion then begin
  866.                 with DisplayPoint do begin
  867.                         h := PicLeftBase;
  868.                         v := PicTopBase;
  869.                     end;
  870.                 with frect do begin
  871.                         left := PicLeftBase + wleft;
  872.                         top := PicTopBase + wtop;
  873.                         right := left + width;
  874.                         bottom := top + height;
  875.                     end;
  876.             end
  877.         else
  878.             with frect do begin
  879.                     left := wleft;
  880.                     top := wtop;
  881.                     right := left + width;
  882.                     bottom := top + height;
  883.                 end;
  884.         if not NewPicWindow('Frames', width, height) then
  885.             exit(CaptureFrames);
  886.         if not MakeStackFromWindow then
  887.             exit(CaptureFrames);
  888.         UpdateTitleBar;
  889.         if FrameGrabber = Scion then begin
  890.                 HideCursor;
  891.                 MainDevice := GetMainDevice;
  892.                 SourcePixMap := MainDevice^^.gdPMap;
  893.             end
  894.         else begin
  895.                 ShowWatch;
  896.                 SourcePixMap := fgPort^.portPixMap;
  897.                 ResetFrameGrabber;
  898.             end;
  899.         FlushEvents(EveryEvent, 0);
  900.         ExternalTrigger := false;
  901.         UpdateVideoControl;
  902.         with info^, info^.StackInfo^ do begin
  903.                 ShowMessage(CmdPeriodToStop);
  904.                 OutOfMemory := false;
  905.                 AdvanceFrame := false;
  906.                 while (not OutOfMemory) and (CurrentSlice <= MaxSlices) do begin
  907.                         if AdvanceFrame then begin
  908.                                 OutOfMemory := not AddSlice(false);
  909.                                 AdvanceFrame := false;
  910.                             end;
  911.                         if FrameGrabber = Scion then begin
  912.                                 GetScionFrame(DisplayPoint);
  913.                                 CheckButton;
  914.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  915.                                 CheckButton;
  916.                             end
  917.                         else begin
  918.                                 GetFrame;
  919.                                 CheckButton;
  920.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  921.                                 CheckButton;
  922.                                 UpdatePicWindow;
  923.                                 CheckButton;
  924.                             end;
  925.                         b := WaitNextEvent(EveryEvent, Event, 0, nil);
  926.                         if event.what = KeyDown then
  927.                             leave;
  928.                     end; {while}
  929.             end; {with}
  930.         if ShutterSound <> nil then
  931.             ReleaseResource(ShutterSound);
  932.     end;
  933.  
  934.  
  935.     procedure CopyPics (sPort, dPort: cGrafPtr; sRect, dRect: rect);
  936.     begin
  937.         pmForeColor(BlackIndex);
  938.         pmBackColor(WhiteIndex);
  939.         hlock(handle(sPort^.portPixMap));
  940.         hlock(handle(dPort^.portPixMap));
  941.         CopyBits(BitMapHandle(sPort^.portPixMap)^^, BitMapHandle(dPort^.PortPixMap)^^, sRect, dRect, SrcCopy, nil);
  942.         hunlock(handle(sPort^.portPixMap));
  943.         hunlock(handle(dPort^.PortPixMap));
  944.         pmForeColor(ForegroundIndex);
  945.         pmBackColor(BackgroundIndex);
  946.     end;
  947.  
  948.  
  949.     procedure MakeMontage;
  950.   {Opens a new window and creates a composite image}
  951.   {from the slices in the current stack.}
  952.         const
  953.             ColumnsID = 3;
  954.             RowsID = 4;
  955.             ScaleID = 5;
  956.             FirstID = 6;
  957.             LastID = 7;
  958.             IncrementID = 8;
  959.             NumberID = 9;
  960.         var
  961.             mylog: DialogPtr;
  962.             item, i, nRows, nColumns, Inc, slices: integer;
  963.             StackWidth, StackHeight, mWidth, mHeight, Background: integer;
  964.             dWidth, dHeight, dLeft, dTop, dRight, dBottom, MaxWidth, MaxHeight: integer;
  965.             FirstSlice, LastSlice, TotalSlices: integer;
  966.             scale, SaveScale: extended;
  967.             sPort, dPort: cGrafPtr;
  968.             StackInfo, MontageInfo: InfoPtr;
  969.             sRect, dRect: rect;
  970.             NumberSlices, IncrementSet: boolean;
  971.             str: str255;
  972.             loc: point;
  973.  
  974.         procedure Estimate (adjustinc: boolean);
  975.             var
  976.                 tmp, xScale, yScale: extended;
  977.                 n: integer;
  978.         begin
  979.             slices := LastSlice - FirstSlice + 1;
  980.             if adjustinc then
  981.                 inc := 0;
  982.             repeat
  983.                 if adjustinc then
  984.                     inc := inc + 1;
  985.                 n := trunc(slices / inc);
  986.                 tmp := sqrt(n);
  987.                 if trunc(tmp) <> tmp then
  988.                     tmp := trunc(tmp) + 1.0;
  989.                 nColumns := trunc(tmp);
  990.                 nRows := nColumns;
  991.                 if (nColumns * (nRows - 1)) >= n then
  992.                     nRows := nRows - 1;
  993.                 xScale := (MaxWidth / nColumns) / StackWidth;
  994.                 yScale := (MaxHeight / nRows) / StackHeight;
  995.                 if xScale < yScale then
  996.                     scale := xScale
  997.                 else
  998.                     scale := yScale;
  999.                 if scale > 1.0 then
  1000.                     scale := 1.0;
  1001.                 SaveScale := scale;
  1002.             until (scale >= 0.5) or (inc >= 3) or not adjustinc;
  1003.         end;
  1004.  
  1005.     begin
  1006.         InitCursor;
  1007.         with info^ do begin
  1008.                 StackWidth := PixelsPerLine;
  1009.                 StackHeight := nLines;
  1010.                 FirstSlice := 1;
  1011.                 TotalSlices := StackInfo^.nSlices;
  1012.                 LastSlice := TotalSlices;
  1013.             end;
  1014.         MaxWidth := ScreenWidth - 85;
  1015.         MaxHeight := ScreenHeight - 45;
  1016.         Estimate(true);
  1017.         NumberSlices := true;
  1018.         IncrementSet := false;
  1019.         mylog := GetNewDialog(150, nil, pointer(-1));
  1020.         SetDNum(MyLog, RowsID, nRows);
  1021.         SetDNum(MyLog, ColumnsID, nColumns);
  1022.         SetDReal(MyLog, ScaleID, scale, 2);
  1023.         SetDNum(MyLog, FirstID, FirstSlice);
  1024.         SetDNum(MyLog, LastID, LastSlice);
  1025.         SetDNum(MyLog, IncrementID, inc);
  1026.         SetDialogItem(MyLog, NumberID, ord(NumberSlices));
  1027.         OutlineButton(MyLog, ok, 16);
  1028.         repeat
  1029.             ModalDialog(nil, item);
  1030.             if item = ColumnsID then begin
  1031.                     nColumns := GetDNum(MyLog, ColumnsID);
  1032.                     if nColumns < 0 then begin
  1033.                             nColumns := 0;
  1034.                             SetDNum(MyLog, ColumnsID, nRows);
  1035.                         end;
  1036.                 end;
  1037.             if item = RowsID then begin
  1038.                     nRows := GetDNum(MyLog, RowsID);
  1039.                     if nRows < 0 then begin
  1040.                             nRows := 0;
  1041.                             SetDNum(MyLog, RowsID, nRows);
  1042.                         end;
  1043.                 end;
  1044.             if item = ScaleID then
  1045.                 scale := GetDReal(MyLog, ScaleID);
  1046.             if item = FirstID then begin
  1047.                     FirstSlice := GetDNum(MyLog, FirstID);
  1048.                     if (FirstSlice < 1) or (FirstSlice > LastSlice) then
  1049.                         FirstSlice := 1;
  1050.                     if IncrementSet then
  1051.                         Estimate(false)
  1052.                     else
  1053.                         Estimate(true);
  1054.                     SetDNum(MyLog, RowsID, nRows);
  1055.                     SetDNum(MyLog, ColumnsID, nColumns);
  1056.                     SetDReal(MyLog, ScaleID, scale, 2);
  1057.                 end;
  1058.             if item = LastID then begin
  1059.                     LastSlice := GetDNum(MyLog, LastID);
  1060.                     if (LastSlice < FirstSlice) or (LastSlice > TotalSlices) then
  1061.                         LastSlice := TotalSlices;
  1062.                     if IncrementSet then
  1063.                         Estimate(false)
  1064.                     else
  1065.                         Estimate(true);
  1066.                     SetDNum(MyLog, RowsID, nRows);
  1067.                     SetDNum(MyLog, ColumnsID, nColumns);
  1068.                     SetDReal(MyLog, ScaleID, scale, 2);
  1069.                 end;
  1070.             if item = IncrementID then begin
  1071.                     inc := GetDNum(MyLog, IncrementID);
  1072.                     IncrementSet := true;
  1073.                     if (inc < 1) or (inc > (slices div 2)) then begin
  1074.                             inc := 1;
  1075.                             SetDNum(MyLog, IncrementID, inc);
  1076.                         end;
  1077.                     Estimate(false);
  1078.                     SetDNum(MyLog, RowsID, nRows);
  1079.                     SetDNum(MyLog, ColumnsID, nColumns);
  1080.                     SetDReal(MyLog, ScaleID, scale, 2);
  1081.                 end;
  1082.             if item = NumberID then begin
  1083.                     NumberSlices := not NumberSlices;
  1084.                     SetDialogItem(MyLog, NumberID, ord(NumberSlices));
  1085.                 end;
  1086.         until (item = ok) or (item = cancel);
  1087.         DisposDialog(mylog);
  1088.         if item = cancel then
  1089.             exit(MakeMontage);
  1090.         if (scale <= 0.05) or (scale > 5) then
  1091.             scale := SaveScale;
  1092.         dWidth := round(StackWidth * scale);
  1093.         dHeight := round(StackHeight * scale);
  1094.         mWidth := nColumns * dWidth;
  1095.         mHeight := nRows * dHeight;
  1096.         StackInfo := info;
  1097.         Background := MyGetPixel(0, 0);
  1098.         SetBackgroundColor(Background);
  1099.         if Background = WhiteIndex then
  1100.             SetForegroundColor(BlackIndex)
  1101.         else
  1102.             SetForegroundColor(WhiteIndex);
  1103.         if not NewPicWindow('Montage', mWidth, mHeight) then
  1104.             exit(MakeMontage);
  1105.         MontageInfo := info;
  1106.         if NumberSlices then begin
  1107.                 SetPort(GrafPtr(info^.osPort));
  1108.                 pmForeColor(ForegroundIndex);
  1109.                 TextFont(ApplFont);
  1110.                 TextSize(9);
  1111.             end;
  1112.         dPort := info^.osPort;
  1113.         dLeft := 0;
  1114.         dTop := 0;
  1115.         sPort := StackInfo^.osPort;
  1116.         sRect := StackInfo^.PicRect;
  1117.         i := FirstSlice;
  1118.         while i <= LastSlice do begin
  1119.                 Info := StackInfo;
  1120.                 SelectSlice(i);
  1121.                 SetRect(dRect, dLeft, dTop, dLeft + dWidth, dTop + DHeight);
  1122.                 CopyPics(sPort, dPort, sRect, dRect);
  1123.                 info := MontageInfo;
  1124.                 if NumberSlices then begin
  1125.                         MoveTo(dLeft + (dWidth div 2) - 3, dTop + dHeight - 9);
  1126.                         NumToString(i, str);
  1127.                         loc.h := dLeft + (dWidth div 2) - 3;
  1128.                         loc.v := dTop + dHeight - 5;
  1129.                         DrawTextString(str, loc, TeJustCenter);
  1130.                     end;
  1131.                 UpdateScreen(dRect);
  1132.                 dLeft := dLeft + dWidth;
  1133.                 if (dLeft + dWidth) > mWidth then begin
  1134.                         dLeft := 0;
  1135.                         dTop := dTop + dHeight;
  1136.                     end;
  1137.                 i := i + inc;
  1138.             end;
  1139.         info := StackInfo;
  1140.         SelectSlice(info^.StackInfo^.CurrentSlice);
  1141.         if MontageInfo^.PixMapSize > UndoBufSize then
  1142.             PutWarning;
  1143.     end;
  1144.  
  1145.  
  1146.     procedure CopyRGBToPixMap (pmap: PixMapHandle);
  1147.         type
  1148.             LongPtr = ^LongInt;
  1149.         var
  1150.             row, i, width: integer;
  1151.             RedLine, GreenLine, BlueLine: LineType;
  1152.             Pixel, RowOffset: LongInt;
  1153.             pmapPtr: ptr;
  1154.             LPtr, RowStart: LongPtr;
  1155.     begin
  1156.         with info^ do begin
  1157.                 pmapPtr := GetPixBaseAddr(pmap);
  1158.                 if pmapPtr = nil then
  1159.                     exit(CopyRGBToPixMap);
  1160.                 LPtr := LongPtr(pmapPtr);
  1161.                 RowStart := LPtr;
  1162.                 RowOffset := band(pmap^^.RowBytes, $1FFF);
  1163.                 width := PicRect.right;
  1164.                 for row := 0 to nLines - 1 do begin
  1165.                         SelectSlice(1);
  1166.                         GetLine(0, row, width, RedLine);
  1167.                         SelectSlice(2);
  1168.                         GetLine(0, row, width, GreenLine);
  1169.                         SelectSlice(3);
  1170.                         GetLine(0, row, width, BlueLine);
  1171.                         LPtr := RowStart;
  1172.                         for i := 0 to PixelsPerLine - 1 do begin
  1173.                                 pixel := -1;
  1174.                                 pixel := RedLine[i];
  1175.                                 pixel := bor(bsl(pixel, 8), GreenLine[i]);
  1176.                                 pixel := bor(bsl(pixel, 8), blueLine[i]);
  1177.                                 LPtr^ := BitNot(pixel);
  1178.                                 LPtr := LongPtr(ord4(LPtr) + 4);
  1179.                             end;
  1180.                         RowStart := LongPtr(ord4(RowStart) + RowOffset);
  1181.                     end;
  1182.                 SelectSlice(StackInfo^.CurrentSlice);
  1183.             end; {with}
  1184.     end;
  1185.  
  1186.  
  1187.     function DoColorOptions: boolean;
  1188.         const
  1189.             ExistingID = 4;
  1190.             SystemID = 5;
  1191.             CustomID = 6;
  1192.             DitherID = 7;
  1193.         var
  1194.             mylog: DialogPtr;
  1195.             item: integer;
  1196.  
  1197.         procedure UpdateButtons;
  1198.         begin
  1199.             SetDialogItem(mylog, ExistingID, ord(RGBLut = ExistingLUT));
  1200.             SetDialogItem(mylog, SystemID, ord(RGBLut = SystemLUT));
  1201.             SetDialogItem(mylog, CustomID, ord(RGBLut = CustomLUT));
  1202.         end;
  1203.  
  1204.     begin
  1205.         InitCursor;
  1206.         mylog := GetNewDialog(160, nil, pointer(-1));
  1207.         SetDialogItem(mylog, DitherID, ord(DitherColor));
  1208.         UpdateButtons;
  1209.         OutlineButton(MyLog, ok, 16);
  1210.         repeat
  1211.             ModalDialog(nil, item);
  1212.             if item = DitherID then begin
  1213.                     DitherColor := not DitherColor;
  1214.                     SetDialogItem(mylog, DitherID, ord(DitherColor));
  1215.                 end;
  1216.             if item = ExistingID then begin
  1217.                     RGBLut := ExistingLUT;
  1218.                     UpdateButtons
  1219.                 end;
  1220.             if item = SystemID then begin
  1221.                     RGBLut := SystemLUT;
  1222.                     UpdateButtons;
  1223.                     DitherColor := true;
  1224.                     SetDialogItem(mylog, DitherID, ord(DitherColor));
  1225.                 end;
  1226.             if item = CustomID then begin
  1227.                     RGBLut := CustomLUT;
  1228.                     UpdateButtons
  1229.                 end;
  1230.         until (item = ok) or (item = cancel);
  1231.         DisposDialog(mylog);
  1232.         DoColorOptions := item <> cancel;
  1233.     end;
  1234.  
  1235.  
  1236.  
  1237.     function Activate (name: str255): boolean;
  1238.   {Activates the window with the specified name.}
  1239.         var
  1240.             i: integer;
  1241.             TempInfo: InfoPtr;
  1242.     begin
  1243.         Activate := false;
  1244.         for i := 1 to nPics do begin
  1245.                 TempInfo := pointer(WindowPeek(PicWindow[i])^.RefCon);
  1246.                 if TempInfo^.title = name then begin
  1247.                         if PicWindow[i] <> nil then begin
  1248.                                 SelectWindow(PicWindow[i]);
  1249.                                 Info := TempInfo;
  1250.                                 ActivateWindow;
  1251.                                 Activate := true;
  1252.                             end; {if}
  1253.                         leave;
  1254.                     end; {if}
  1255.             end; {for}
  1256.     end;
  1257.  
  1258.  
  1259.     procedure ConvertRGBToEightBitColor (Capturing: boolean);
  1260.         var
  1261.             err: QDErr;
  1262.             err2: OSErr;
  1263.             osGWorld: GWorldPtr;
  1264.             aGDevice: GDHandle;
  1265.             flags: GWorldFlags;
  1266.             pmap: PixMapHandle;
  1267.             pRect: rect;
  1268.             thePictInfo: PictInfo;
  1269.             CopyMode, SamplingMethod: integer;
  1270.             UpdateNeeded: boolean;
  1271.  
  1272.         procedure abort;
  1273.         begin
  1274.             DisposeGWorld(osGWorld);
  1275.             exit(ConvertRGBToEightBitColor);
  1276.         end;
  1277.  
  1278.     begin
  1279.         if not System7 then begin
  1280.                 PutMessage('You must be running System 7 to do 24 to 8-bit color conversions.');
  1281.                 exit(ConvertRGBToEightBitColor);
  1282.             end;
  1283.         with info^ do begin
  1284.                 if StackInfo^.nSlices <> 3 then begin
  1285.                         PutMessage('24 to 8-bit color conversion requires a three slice(red, green and blue) stack as input.');
  1286.                         exit(ConvertRGBToEightBitColor);
  1287.                     end;
  1288.                 if Capturing then begin
  1289.                         DitherColor := true;
  1290.                         RGBLut := CustomLUT;
  1291.                     end
  1292.                 else if not macro then begin
  1293.                         if not DoColorOptions then
  1294.                             exit(ConvertRGBToEightBitColor);
  1295.                     end;
  1296.                 ShowWatch;
  1297.                 flags := [];
  1298.                 err := NewGWorld(osGWorld, 32, PicRect, nil, aGDevice, flags);
  1299.                 if err <> NoErr then begin
  1300.                         PutMemoryAlert;
  1301.                         exit(ConvertRGBToEightBitColor);
  1302.                     end;
  1303.                 pmap := GetGWorldPixMap(osGWorld);
  1304.                 if not LockPixels(pmap) then
  1305.                     abort;
  1306.                 CopyRGBToPixMap(pmap);
  1307.                 pRect := PicRect;
  1308.             end; {with}
  1309.         UpdateNeeded := true;
  1310.         if Activate('Indexed Color') then begin
  1311.                 if (info^.PixelsPerLine <> pRect.right) or (info^.nLines <> pRect.bottom) then begin
  1312.                         if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then
  1313.                             abort;
  1314.                         UpdateNeeded := false;
  1315.                     end
  1316.             end
  1317.         else begin
  1318.                 if not NewPicWindow('Indexed Color', pRect.right, pRect.bottom) then
  1319.                     abort;
  1320.                 UpdateNeeded := false;
  1321.             end;
  1322.         if RGBLut = SystemLUT then
  1323.             SwitchColorTables(SystemPaletteItem, false)
  1324.         else if RGBLut = CustomLut then begin
  1325.                 if OptionKeyWasDown then
  1326.                     SamplingMethod := PopularMethod
  1327.                 else
  1328.                     SamplingMethod := SystemMethod;
  1329.                 err2 := GetPixMapInfo(pmap, thePictInfo, ReturnColorTable, 256, SamplingMethod, 0);
  1330.                 LoadColorTable(thePictInfo.theColorTable);
  1331.             end;
  1332.         SetForegroundColor(BlackIndex);
  1333.         SetBackgroundColor(WhiteIndex);
  1334.         if DitherColor then
  1335.             CopyMode := DitherCopy
  1336.         else
  1337.             CopyMode := SrcCopy;
  1338.         CopyBits(BitMapHandle(pmap)^^, BitMapHandle(info^.osPort^.PortPixMap)^^, pRect, pRect, CopyMode, nil);
  1339.         DisposeGWorld(osGWorld);
  1340.         if UpdateNeeded then
  1341.             UpdatePicWindow;
  1342.     end;
  1343.  
  1344.  
  1345.     function MakeRGBStack (name: str255): boolean;
  1346.         var
  1347.             ignore: integer;
  1348.     begin
  1349.         MakeRGBStack := false;
  1350.         if not Duplicate(name, false) then
  1351.             exit(MakeRGBStack);
  1352.         if not MakeStackFromWindow then
  1353.             exit(MakeRGBStack);
  1354.         if not AddSlice(false) then begin
  1355.                 info^.changes := false;
  1356.                 ignore := CloseAWindow(info^.wptr);
  1357.                 exit(MakeRGBStack);
  1358.             end;
  1359.         if not AddSlice(false) then begin
  1360.                 info^.changes := false;
  1361.                 ignore := CloseAWindow(info^.wptr);
  1362.                 exit(MakeRGBStack);
  1363.             end;
  1364.         MakeRGBStack := true;
  1365.     end;
  1366.  
  1367.  
  1368.     procedure ConvertEightBitColorToRGB;
  1369.         var
  1370.             width, height, i, row: integer;
  1371.             srcLine, rLine, gLine, bLine: LineType;
  1372.             rLut, gLUT, bLUT: packed array[0..255] of byte;
  1373.             value: byte;
  1374.     begin
  1375.         if isGrayscaleLUT then begin
  1376.                 PutMessage('8-bit color to RGB conversion requires a color image.');
  1377.                 exit(ConvertEightBitColorToRGB);
  1378.             end;
  1379.         KillRoi;
  1380.         if not MakeRGBStack(concat(info^.title, '(RGB)')) then
  1381.             exit(ConvertEightBitColorToRGB);
  1382.         LoadLUT(Info^.cTable);
  1383.         for i := 0 to 255 do
  1384.             with cScreenPort^.portPixMap^^.pmTable^^.ctTable[i].rgb do begin
  1385.                     rLUT[i] := BitNot(band(bsr(red, 8), 255));
  1386.                     gLUT[i] := BitNot(band(bsr(green, 8), 255));
  1387.                     bLUT[i] := BitNot(band(bsr(blue, 8), 255));
  1388.                 end;
  1389.         width := info^.PixelsPerLine;
  1390.         height := info^.nLines;
  1391.         for row := 0 to height - 1 do begin
  1392.                 SelectSlice(1);
  1393.                 GetLine(0, row, width, srcLine);
  1394.                 for i := 0 to width - 1 do begin
  1395.                         value := srcLine[i];
  1396.                         rLine[i] := rLUT[value];
  1397.                         gLine[i] := gLUT[value];
  1398.                         bLine[i] := bLUT[value];
  1399.                     end;
  1400.                 PutLine(0, row, width, rLine);
  1401.                 SelectSlice(2);
  1402.                 PutLine(0, row, width, gLine);
  1403.                 SelectSlice(3);
  1404.                 PutLine(0, row, width, bLine);
  1405.             end;
  1406.         with Info^.StackInfo^ do begin
  1407.                 CurrentSlice := 1;
  1408.                 SelectSlice(CurrentSlice);
  1409.                 UpdateTitleBar;
  1410.             end;
  1411.     end;
  1412.  
  1413.  
  1414.     procedure CaptureColor;
  1415.         var
  1416.             MainDevice: GDHandle;
  1417.             SourcePixMap: PixMapHandle;
  1418.             frame, width, height, SaveChannel: integer;
  1419.             frect: rect;
  1420.             DisplayPoint: point;
  1421.     begin
  1422.         with info^ do
  1423.             if (PictureType <> FrameGrabberType) and (PictureType <> ScionType) then begin
  1424.                     PutMessage('You must be capturing to capture color.');
  1425.                     macro := false;
  1426.                     exit(CaptureColor);
  1427.                 end;
  1428.         StopDigitizing;
  1429.         with info^.PicRect do begin
  1430.                 width := right - left;
  1431.                 height := bottom - top;
  1432.             end;
  1433.         if Activate('RGB') then
  1434.             with info^.PicRect do begin
  1435.                     if ((right - left) <> width) or ((bottom - top) <> height) then
  1436.                         if not MakeRGBStack('RGB') then
  1437.                             exit(CaptureColor);
  1438.                 end
  1439.         else if not MakeRGBStack('RGB') then
  1440.             exit(CaptureColor);
  1441.         if FrameGrabber = Scion then begin
  1442.                 HideCursor;
  1443.                 MainDevice := GetMainDevice;
  1444.                 SourcePixMap := MainDevice^^.gdPMap;
  1445.             end
  1446.         else begin
  1447.                 ShowWatch;
  1448.                 SourcePixMap := fgPort^.portPixMap;
  1449.                 ResetFrameGrabber;
  1450.             end;
  1451.         if FrameGrabber = Scion then begin
  1452.                 with DisplayPoint do begin
  1453.                         h := PicLeftBase;
  1454.                         v := PicTopBase;
  1455.                     end;
  1456.                 with frect do begin
  1457.                         left := PicLeftBase;
  1458.                         top := PicTopBase;
  1459.                         right := left + width;
  1460.                         bottom := top + height;
  1461.                     end;
  1462.             end
  1463.         else
  1464.             with frect do begin
  1465.                     left := 0;
  1466.                     top := 0;
  1467.                     right := left + width;
  1468.                     bottom := top + height;
  1469.                 end;
  1470.         ShowTriggerMessage;
  1471.         SaveChannel := VideoChannel;
  1472.         with info^, info^.StackInfo^ do begin
  1473.                 for frame := 1 to 3 do begin
  1474.                         if FrameGrabber = QuickCapture then begin
  1475.                                 case frame of
  1476.                                     1: 
  1477.                                         VideoChannel := 1; {Green}
  1478.                                     2: 
  1479.                                         VideoChannel := 0;  {Red}
  1480.                                     3: 
  1481.                                         VideoChannel := 2;  {Blue}
  1482.                                 end;
  1483.                                 ResetFrameGrabber;
  1484.                                 repeat
  1485.                                 until band(ControlReg^, $8) = 0; {mux channel not busy}
  1486.                             end
  1487.                         else begin
  1488.                                 VideoChannel := frame - 1;
  1489.                                 ResetFrameGrabber;
  1490.                             end;
  1491.                         if VideoControl <> nil then
  1492.                             ShowChannel;
  1493.                         CurrentSlice := frame;
  1494.                         SelectSlice(CurrentSlice);
  1495.                         if FrameGrabber = Scion then begin
  1496.                                 GetScionFrame(DisplayPoint);
  1497.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  1498.                             end
  1499.                         else begin
  1500.                                 GetFrame;
  1501.                                 CopyOffscreen(SourcePixMap, osPort^.portPixMap, frect, PicRect);
  1502.                             end;
  1503.                     end; {for}
  1504.                 CurrentSlice := 1;
  1505.                 SelectSlice(CurrentSlice);
  1506.                 UpdateTitleBar;
  1507.             end; {with}
  1508.         VideoChannel := SaveChannel;
  1509.         if VideoControl <> nil then
  1510.             ShowChannel;
  1511.         ConvertRGBToEightBitColor(true);
  1512.     end;
  1513.  
  1514.  
  1515.     procedure AverageSlices;
  1516.         const
  1517.             MaxWidth = 2048;
  1518.         var
  1519.             slices, sRow, aRow, slice, i, SaveSlice: integer;
  1520.             width, height, hstart, vStart: integer;
  1521.             OldInfo, NewInfo: InfoPtr;
  1522.             aLine: LineType;
  1523.             mask: rect;
  1524.             sum: array[0..MaxWidth] of LongInt;
  1525.             AutoSelectAll: boolean;
  1526.     begin
  1527.         OldInfo := Info;
  1528.         with info^ do begin
  1529.                 if StackInfo = nil then begin
  1530.                         PutMessage('Average Slices requires a stack.');
  1531.                         macro := false;
  1532.                         exit(AverageSlices);
  1533.                     end;
  1534.                 AutoSelectAll := not Info^.RoiShowing;
  1535.                 if AutoSelectAll then
  1536.                     SelectAll(true);
  1537.                 with RoiRect do begin
  1538.                         hStart := left;
  1539.                         vStart := top;
  1540.                         width := right - left;
  1541.                         height := bottom - top;
  1542.                     end;
  1543.                 if width > MaxWidth then begin
  1544.                         PutMessage(concat('Image can''t average selections wider than ', Long2str(MaxWidth), ' pixels.'));
  1545.                         macro := false;
  1546.                         exit(AverageSlices);
  1547.                     end;
  1548.                 with StackInfo^ do begin
  1549.                         slices := StackInfo^.nSlices;
  1550.                         SaveSlice := CurrentSlice;
  1551.                     end;
  1552.                 if not NewPicWindow('Average', width, height) then begin
  1553.                         macro := false;
  1554.                         exit(AverageSlices);
  1555.                     end;
  1556.             end;
  1557.         info^.changes := true;
  1558.         NewInfo := Info;
  1559.         aRow := 0;
  1560.         for sRow := vStart to vStart + height - 1 do begin
  1561.                 info := OldInfo;
  1562.                 for i := 0 to width - 1 do
  1563.                     sum[i] := 0;
  1564.                 for slice := 1 to slices do begin
  1565.                         SelectSlice(slice);
  1566.                         GetLine(hStart, sRow, width, aLine);
  1567.                         for i := 0 to width - 1 do
  1568.                             sum[i] := sum[i] + aLine[i];
  1569.                     end;
  1570.                 for i := 0 to width - 1 do
  1571.                     aLine[i] := sum[i] div slices;
  1572.                 info := NewInfo;
  1573.                 PutLine(0, aRow, width, aLine);
  1574.                 SetRect(mask, 0, aRow, width, aRow + 1);
  1575.                 aRow := aRow + 1;
  1576.                 UpdateScreen(mask);
  1577.                 if CommandPeriod then
  1578.                     leave;
  1579.             end;
  1580.         info := OldInfo;
  1581.         SelectSlice(SaveSlice);
  1582.         if AutoSelectAll then
  1583.             KillRoi;
  1584.     end;
  1585.  
  1586.  
  1587.     procedure ConvertRGBToHSV;
  1588.         const
  1589.             MaxSaturation = 255;
  1590.             MaxValue = 255;
  1591.         var
  1592.             width, height, i, row, mark: integer;
  1593.             rLine, gLine, bLine, hLine, sLine, vLine: LineType;
  1594.             delta, min, max, R, G, B, H, S, V: integer;
  1595.             tmp: longint;
  1596.             UpdateR: rect;
  1597.  
  1598.         function Max3 (a, b, c: integer): integer;
  1599.             var
  1600.                 TempMax: integer;
  1601.         begin
  1602.             if (a > b) then
  1603.                 TempMax := a
  1604.             else
  1605.                 TempMax := b;
  1606.             if (TempMax > c) then
  1607.                 Max3 := TempMax
  1608.             else
  1609.                 Max3 := c;
  1610.         end;
  1611.  
  1612.         function Min3 (a, b, c: integer): integer;
  1613.             var
  1614.                 TempMin: integer;
  1615.         begin
  1616.             if (a < b) then
  1617.                 TempMin := a
  1618.             else
  1619.                 TempMin := b;
  1620.             if (TempMin < c) then
  1621.                 Min3 := TempMin
  1622.             else
  1623.                 Min3 := c;
  1624.         end;
  1625.  
  1626.     begin
  1627.         with info^ do begin
  1628.                 if StackInfo^.nSlices <> 3 then begin
  1629.                         PutMessage('RGB to HSV color conversion requires a three slice(red, green and blue) stack as input.');
  1630.                         exit(ConvertRGBToHSV);
  1631.                     end;
  1632.                 if Changes then begin
  1633.                         if PutMessageWithCancel('RGB to HSV color conversion is undoable.') = cancel then
  1634.                             exit(ConvertRGBToHSV);
  1635.                     end;
  1636.                 KillRoi;
  1637.                 with StackInfo^ do begin
  1638.                         CurrentSlice := 1;
  1639.                         SelectSlice(CurrentSlice);
  1640.                         UpdatePicWindow;
  1641.                     end;
  1642.                 SwitchColorTables(SpectrumItem, true);
  1643.                 title := 'HSV';
  1644.                 UpdateTitleBar;
  1645.                 width := PixelsPerLine;
  1646.                 height := nLines;
  1647.                 mark := 0;
  1648.                 ShowWatch;
  1649.                 for row := 0 to height - 1 do begin
  1650.                         SelectSlice(1);
  1651.                         GetLine(0, row, width, rLine);
  1652.                         SelectSlice(2);
  1653.                         GetLine(0, row, width, gLine);
  1654.                         SelectSlice(3);
  1655.                         GetLine(0, row, width, bLine);
  1656.                         for i := 0 to width - 1 do begin
  1657.                                 R := 255 - rLine[i];
  1658.                                 G := 255 - gLine[i];
  1659.                                 B := 255 - bLine[i];
  1660.                                 max := Max3(R, G, B);
  1661.                                 min := Min3(R, G, B);
  1662.                                 V := max;
  1663.                                 if max <> 0 then begin
  1664.                                         tmp := 255 * (max - min);
  1665.                                         S := (tmp + (tmp mod max)) div max;  {adding '(tmp mod max)' simulate rounding}
  1666.                                     end
  1667.                                 else
  1668.                                     S := 0;
  1669.                                 if S = 0 then
  1670.                                     H := 0  {undefined but, but select red }
  1671.                                 else begin
  1672.                                         delta := max - min;
  1673.                                         if R = max then begin
  1674.                                                 tmp := 85 * (G - B);
  1675.                                                 H := tmp div delta;
  1676.                                             end
  1677.                                         else if G = max then begin
  1678.                                                 tmp := 85 * (B - R);
  1679.                                                 H := 170 + tmp div delta;
  1680.                                             end
  1681.                                         else if B = max then begin
  1682.                                                 tmp := 85 * (R - G);
  1683.                                                 H := 340 + tmp div delta;
  1684.                                             end;
  1685.                                         H := H div 2;
  1686.                                         if H < 0 then
  1687.                                             H := H + 255
  1688.                                     end;
  1689.                                 if H = 0 then
  1690.                                     hLine[i] := 1
  1691.                                 else
  1692.                                     hLine[i] := H;
  1693.                                 sLine[i] := S;
  1694.                                 vLine[i] := 255 - V;
  1695.                             end;
  1696.                         SelectSlice(1);
  1697.                         PutLine(0, row, width, hLine);
  1698.                         if (row mod 10) = 0 then begin
  1699.                                 setrect(UpdateR, 0, mark, width - 1, row);
  1700.                                 mark := row;
  1701.                                 UpdateScreen(UpdateR);
  1702.                             end;
  1703.                         SelectSlice(2);
  1704.                         PutLine(0, row, width, sLine);
  1705.                         SelectSlice(3);
  1706.                         PutLine(0, row, width, vLine);
  1707.                     end;
  1708.                 SelectSlice(1);
  1709.             end; {with}
  1710.         WhatToUndo := NothingToUndo;
  1711.     end;
  1712.  
  1713.  
  1714. end.